#region Copyright (c) 2006-2010 Widgetsphere LLC, All Rights Reserved
//--------------------------------------------------------------------- *
//                          Widgetsphere  LLC                           *
//             Copyright (c) 2006-2010 All Rights reserved              *
//                                                                      *
//                                                                      *
//This file and its contents are protected by United States and         *
//International copyright laws.  Unauthorized reproduction and/or       *
//distribution of all or any portion of the code contained herein       *
//is strictly prohibited and will result in severe civil and criminal   *
//penalties.  Any violations of this copyright will be prosecuted       *
//to the fullest extent possible under law.                             *
//                                                                      *
//THE SOURCE CODE CONTAINED HEREIN AND IN RELATED FILES IS PROVIDED     *
//TO THE REGISTERED DEVELOPER FOR THE PURPOSES OF EDUCATION AND         *
//TROUBLESHOOTING. UNDER NO CIRCUMSTANCES MAY ANY PORTION OF THE SOURCE *
//CODE BE DISTRIBUTED, DISCLOSED OR OTHERWISE MADE AVAILABLE TO ANY     *
//THIRD PARTY WITHOUT THE EXPRESS WRITTEN CONSENT OF WIDGETSPHERE LLC   *
//                                                                      *
//UNDER NO CIRCUMSTANCES MAY THE SOURCE CODE BE USED IN WHOLE OR IN     *
//PART, AS THE BASIS FOR CREATING A PRODUCT THAT PROVIDES THE SAME, OR  *
//SUBSTANTIALLY THE SAME, FUNCTIONALITY AS ANY WIDGETSPHERE PRODUCT.    *
//                                                                      *
//THE REGISTERED DEVELOPER ACKNOWLEDGES THAT THIS SOURCE CODE           *
//CONTAINS VALUABLE AND PROPRIETARY TRADE SECRETS OF WIDGETSPHERE,      *
//INC.  THE REGISTERED DEVELOPER AGREES TO EXPEND EVERY EFFORT TO       *
//INSURE ITS CONFIDENTIALITY.                                           *
//                                                                      *
//THE END USER LICENSE AGREEMENT (EULA) ACCOMPANYING THE PRODUCT        *
//PERMITS THE REGISTERED DEVELOPER TO REDISTRIBUTE THE PRODUCT IN       *
//EXECUTABLE FORM ONLY IN SUPPORT OF APPLICATIONS WRITTEN USING         *
//THE PRODUCT.  IT DOES NOT PROVIDE ANY RIGHTS REGARDING THE            *
//SOURCE CODE CONTAINED HEREIN.                                         *
//                                                                      *
//THIS COPYRIGHT NOTICE MAY NOT BE REMOVED FROM THIS FILE.              *
//--------------------------------------------------------------------- *
#endregion
using System;
using System.Linq;
using Widgetsphere.Generator.Common.GeneratorFramework;
using Widgetsphere.Generator.Models;
using System.Text;
using Widgetsphere.Generator.Common.Util;

namespace Widgetsphere.Generator.EFDAL.Generators.EFSSDL
{
	public class SSDLTemplate : EFDALBaseTemplate
	{
		private StringBuilder sb = new StringBuilder();

		public SSDLTemplate(ModelRoot model)
		{
			_model = model;
		}

		#region BaseClassTemplate overrides
		public override string FileName
		{
			get { return string.Format("{0}.ssdl", _model.ProjectName); }
		}


		public override string FileContent
		{
			get
			{
				try
				{
					GenerateContent();
					return sb.ToString();
				}
				catch (Exception ex)
				{
					throw;
				}

			}
		}

		#endregion

		#region GenerateContent

		private void GenerateContent()
		{
			sb.AppendLine("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
			sb.AppendFormat("<Schema Namespace=\"{0}.Store\" Alias=\"Self\" Provider=\"System.Data.SqlClient\" ProviderManifestToken=\"2008\" xmlns:store=\"http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator\" xmlns=\"http://schemas.microsoft.com/ado/2006/04/edm/ssdl\">", this.DefaultNamespace).AppendLine();

			AppendEntityContainer();
			AppendEntityType();
			AppendAssociations();
			AppendFunctions();

			sb.AppendLine("</Schema>");
		}

		private void AppendFunctions()
		{
			sb.AppendLine("	<Function Name=\"gen_NOOP\" Aggregate=\"false\" BuiltIn=\"false\" NiladicFunction=\"false\" IsComposable=\"false\" ParameterTypeSemantics=\"AllowImplicitConversion\" Schema=\"dbo\">");
			sb.AppendLine("	</Function>");

			foreach (Table currentTable in _model.Database.Tables.Where(x => x.Generated))
			{
				//DELETE
				sb.AppendFormat("	<Function Name=\"gen_{0}Delete\" Aggregate=\"false\" BuiltIn=\"false\" NiladicFunction=\"false\" IsComposable=\"false\" ParameterTypeSemantics=\"AllowImplicitConversion\" Schema=\"dbo\">", currentTable.PascalName).AppendLine();
				foreach (var primaryKeyColumn in currentTable.PrimaryKeyColumns)
				{
					sb.AppendFormat("		<Parameter Name=\"Original_{0}\" Type=\"{1}\" Mode=\"In\" />", primaryKeyColumn.DatabaseName, primaryKeyColumn.EFDatabaseType(false)).AppendLine();
				}

				foreach (Relation relation in currentTable.ChildRoleRelations)
				{
					Table parentTable = (Table)relation.ParentTableRef.Object;
					Table childTable = (Table)relation.ChildTableRef.Object;
					//if (relation.FkColumns.Count(x => x.PrimaryKey) == 0)
					//{
						foreach (ColumnRelationship columnRelationship in relation.ColumnRelationships)
						{
							Column parentColumn = (Column)columnRelationship.ParentColumnRef.Object;
							Column childColumn = (Column)columnRelationship.ChildColumnRef.Object;
							sb.AppendFormat("		<Parameter Name=\"{0}\" Type=\"{1}\" Mode=\"In\" />", relation.RoleName + parentTable.DatabaseName + "_" + childColumn.DatabaseName, childColumn.EFDatabaseType(false)).AppendLine();
						}
					//}
				}

				sb.AppendLine("	</Function>");

				//INSERT
				sb.AppendFormat("	<Function Name=\"gen_{0}Insert\" Aggregate=\"false\" BuiltIn=\"false\" NiladicFunction=\"false\" IsComposable=\"false\" ParameterTypeSemantics=\"AllowImplicitConversion\" Schema=\"dbo\">", currentTable.PascalName).AppendLine();
				foreach (Reference columnRef in currentTable.Columns)
				{
					Column currentColumn = (Column)columnRef.Object;
					if (currentColumn.Generated && currentColumn.Identity != IdentityTypeConstants.Database && !currentColumn.ComputedColumn)
						sb.AppendFormat("		<Parameter Name=\"{0}\" Type=\"{1}\" Mode=\"In\" />", currentColumn.DatabaseName, currentColumn.EFDatabaseType(false)).AppendLine();
				}
				if (currentTable.AllowCreateAudit)
				{
					sb.AppendFormat("		<Parameter Name=\"{0}\" Type=\"datetime\" Mode=\"In\" />", _model.Database.CreatedDateColumnName).AppendLine();
					sb.AppendFormat("		<Parameter Name=\"{0}\" Type=\"varchar\" Mode=\"In\" />", _model.Database.CreatedByColumnName).AppendLine();
				}
				if (currentTable.AllowModifiedAudit)
				{
					sb.AppendFormat("		<Parameter Name=\"{0}\" Type=\"varchar\" Mode=\"In\" />", _model.Database.ModifiedByColumnName).AppendLine();
				}
				sb.AppendLine("	</Function>");

				//UPDATE
				if (!currentTable.AssociativeTable)
				{
					sb.AppendLine("	<Function Name=\"gen_" + currentTable.PascalName + "Update" + "\" Aggregate=\"false\" BuiltIn=\"false\" NiladicFunction=\"false\" IsComposable=\"false\" ParameterTypeSemantics=\"AllowImplicitConversion\" Schema=\"dbo\">");
					foreach (Reference columnRef in currentTable.Columns)
					{
						Column currentColumn = (Column)columnRef.Object;
						if (currentColumn.Generated && !currentColumn.PrimaryKey && !currentColumn.ComputedColumn)
							sb.AppendFormat("		<Parameter Name=\"{0}\" Type=\"{1}\" Mode=\"In\" />", currentColumn.DatabaseName, currentColumn.EFDatabaseType(false)).AppendLine();
					}

					if (currentTable.AllowModifiedAudit)
					{
						sb.AppendFormat("		<Parameter Name=\"{0}\" Type=\"datetime\" Mode=\"In\" />", _model.Database.ModifiedDateColumnName).AppendLine();
						sb.AppendFormat("		<Parameter Name=\"{0}\" Type=\"varchar\" Mode=\"In\" />", _model.Database.ModifiedByColumnName).AppendLine();
					}

					foreach (var primaryKeyColumn in currentTable.PrimaryKeyColumns)
					{
						sb.AppendFormat("		<Parameter Name=\"Original_{0}\" Type=\"{1}\" Mode=\"In\" />", primaryKeyColumn.DatabaseName, primaryKeyColumn.EFDatabaseType(false)).AppendLine();
					}
					if (currentTable.AllowTimestamp)
					{
						sb.AppendFormat("		<Parameter Name=\"Original_{0}\" Type=\"timestamp\" Mode=\"In\" />", _model.Database.TimestampColumnName).AppendLine();
					}
					sb.AppendLine("	</Function>");
				}

			}
		}

		private void AppendAssociations()
		{
			foreach (Relation relation in _model.Database.Relations)
			{
				Table parentEntity = (Table)relation.ParentTableRef.Object;
				Table childEntity = (Table)relation.ChildTableRef.Object;
				if (parentEntity != null && childEntity != null && parentEntity.Generated && childEntity.Generated)
				{
					StringBuilder parentColumns = new StringBuilder();
					StringBuilder childColumns = new StringBuilder();
					foreach (ColumnRelationship columnRelation in relation.ColumnRelationships)
					{
						Column childColumn = (Column)columnRelation.ChildColumnRef.Object;
						Column parentColumn = (Column)columnRelation.ParentColumnRef.Object;
						parentColumns.AppendFormat("				<PropertyRef Name=\"{0}\" />", parentColumn.DatabaseName).AppendLine();
						childColumns.AppendFormat("				<PropertyRef Name=\"{0}\" />", childColumn.DatabaseName).AppendLine();
					}

					sb.AppendFormat("	<Association Name=\"{0}\">", relation.GetDatabaseFkName()).AppendLine();
					sb.AppendFormat("		<End Role=\"{0}\" Type=\"{1}.Store.{0}\" Multiplicity=\"{2}\" />", parentEntity.DatabaseName, this.DefaultNamespace, relation.ParentMultiplicity()).AppendLine();
					sb.AppendFormat("		<End Role=\"{0}\" Type=\"{1}.Store.{0}\" Multiplicity=\"{2}\" />", childEntity.DatabaseName, this.DefaultNamespace, relation.ChildMultiplicity()).AppendLine();
					sb.AppendLine("		<ReferentialConstraint>");
					sb.AppendFormat("			<Principal Role=\"{0}\">", parentEntity.DatabaseName).AppendLine();
					sb.Append(parentColumns.ToString());
					sb.AppendLine("			</Principal>");
					sb.AppendFormat("      <Dependent Role=\"{0}\">", childEntity.DatabaseName).AppendLine();
					sb.Append(childColumns.ToString());
					sb.AppendLine("			</Dependent>");
					sb.AppendLine("		</ReferentialConstraint>");
					sb.AppendLine("	</Association>");

				}
			}
		}

		private void AppendEntityType()
		{
			foreach (Table currentTable in _model.Database.Tables.Where(x => x.Generated).OrderBy(x => x.Name))
			{
				sb.AppendFormat("	<EntityType Name=\"{0}\">", currentTable.DatabaseName).AppendLine();
				sb.AppendLine("		<Key>");
				foreach (Column column in currentTable.PrimaryKeyColumns)
				{
					sb.AppendFormat("			<PropertyRef Name=\"{0}\" />", column.DatabaseName).AppendLine();
				}
				sb.AppendLine("		</Key>");

				foreach (Column column in currentTable.GetColumns().Where(x => x.Generated).OrderBy(x => x.Name))
				{
					GeneratePropertyNode(column);
				}
				this.GenerateAuditColumns(currentTable);
				sb.AppendLine("	</EntityType>");
			}
		}

		private void AppendEntityContainer()
		{
			sb.AppendFormat("	<EntityContainer Name=\"{0}ModelStoreContainer\">", _model.ProjectName).AppendLine();
			foreach (Table currentTable in _model.Database.Tables.Where(x => x.Generated).OrderBy(x => x.Name))
			{
				sb.AppendFormat("		<EntitySet Name=\"{0}\" EntityType=\"{1}.Store.{0}\" store:Type=\"Tables\" Schema=\"dbo\" />", currentTable.DatabaseName, this.DefaultNamespace).AppendLine();
			}

			foreach (Relation relation in _model.Database.Relations)
			{
				Table parentEntity = (Table)relation.ParentTableRef.Object;
				Table childEntity = (Table)relation.ChildTableRef.Object;
				if (parentEntity != null && childEntity != null && parentEntity.Generated && childEntity.Generated && relation.IsGenerated)
				{
					sb.AppendFormat("		<AssociationSet Name=\"{0}\" Association=\"{1}.Store.{0}\">", relation.GetDatabaseFkName(), this.DefaultNamespace).AppendLine();
					sb.AppendFormat("			<End Role=\"{0}\" EntitySet=\"{0}\" />", parentEntity.DatabaseName).AppendLine();
					sb.AppendFormat("			<End Role=\"{0}\" EntitySet=\"{0}\" />", childEntity.DatabaseName).AppendLine();
					sb.AppendFormat("		</AssociationSet>").AppendLine();
				}
			}
			sb.AppendLine("	</EntityContainer>");
		}

		private void GenerateAuditColumns(Table currentTable)
		{
			if (currentTable.AllowModifiedAudit)
			{
				sb.AppendFormat("		<Property Name=\"{0}\" Type=\"varchar\" MaxLength=\"50\" />", _model.Database.ModifiedByColumnName).AppendLine();
				sb.AppendFormat("		<Property Name=\"{0}\" Type=\"datetime\" />", _model.Database.ModifiedDateColumnName).AppendLine();
			}

			if (currentTable.AllowCreateAudit)
			{
				sb.AppendFormat("		<Property Name=\"{0}\" Type=\"varchar\" MaxLength=\"50\" />", _model.Database.CreatedByColumnName).AppendLine();
				sb.AppendFormat("		<Property Name=\"{0}\" Type=\"datetime\" />", _model.Database.CreatedDateColumnName).AppendLine();
			}

			if (currentTable.AllowTimestamp)
			{
				sb.AppendFormat("		<Property Name=\"{0}\" Type=\"timestamp\" Nullable=\"false\" StoreGeneratedPattern=\"Computed\" />", _model.Database.TimestampColumnName).AppendLine();
			}
		}

		private void GeneratePropertyNode(Column column)
		{
			//Append Name and Type
			sb.AppendFormat("		<Property Name=\"{0}\" Type=\"{1}\" ", column.DatabaseName, column.EFDatabaseType());
			//Append Nullable
			if (!column.AllowNull)
			{
				sb.Append("Nullable=\"false\" ");
			}
			//Append MaxLength
			if (!string.IsNullOrEmpty(column.EFGetDatabaseMaxLengthString()))
				sb.AppendFormat("MaxLength=\"{0}\" ", column.EFGetDatabaseMaxLengthString());
			//Append StoreGeneratedPattern
			if (!string.IsNullOrEmpty(column.EFStoreGeneratedPattern()))
				sb.AppendFormat("StoreGeneratedPattern=\"{0}\" ", column.EFStoreGeneratedPattern());

			sb.AppendLine("/>");
		}

		#endregion


	}
}